/*
 * Copyright 1999-2011 Alibaba Group.
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.dubbo.remoting.exchange.codec;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

import com.alibaba.dubbo.common.io.Bytes;
import com.alibaba.dubbo.common.io.StreamUtils;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.serialize.ObjectInput;
import com.alibaba.dubbo.common.serialize.ObjectOutput;
import com.alibaba.dubbo.common.serialize.Serialization;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.remoting.Channel;
import com.alibaba.dubbo.remoting.Interceptor;
import com.alibaba.dubbo.remoting.MessageCodec;
import com.alibaba.dubbo.remoting.MessageDecode;
import com.alibaba.dubbo.remoting.MessageEncode;
import com.alibaba.dubbo.remoting.RemoteCalling;
import com.alibaba.dubbo.remoting.RemoteCallingAware;
import com.alibaba.dubbo.remoting.RemotingException;
import com.alibaba.dubbo.remoting.buffer.ChannelBuffer;
import com.alibaba.dubbo.remoting.buffer.ChannelBufferInputStream;
import com.alibaba.dubbo.remoting.buffer.ChannelBufferOutputStream;
import com.alibaba.dubbo.remoting.exchange.Request;
import com.alibaba.dubbo.remoting.exchange.Response;
import com.alibaba.dubbo.remoting.exchange.support.DefaultFuture;
import com.alibaba.dubbo.remoting.interceptor.InterceptorUtils;
import com.alibaba.dubbo.remoting.msgcodec.BridgeDecodeInputStream;
import com.alibaba.dubbo.remoting.msgcodec.BridgeEncodeOutputStream;
import com.alibaba.dubbo.remoting.msgcodec.DecodeWrapper;
import com.alibaba.dubbo.remoting.msgcodec.EncodeWrapper;
import com.alibaba.dubbo.remoting.msgcodec.MessageCodecSupport;
import com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec;
import com.alibaba.dubbo.remoting.transport.CodecSupport;

/**
 * ExchangeCodec.
 * 
 * @author qianlei
 * @author william.liangf
 */
public class ExchangeCodec extends TelnetCodec {

    private static final Logger     logger             = LoggerFactory.getLogger(ExchangeCodec.class);

    // header length. aruan 2014.09.16, change 16->20, add 4 bytes for data size
    protected static final int      HEADER_LENGTH      = 16;
    protected static final int 	    HEADER_LENGTH_EX   = 16 + 4;
    
    protected static final String ATTR_ENCODE = "ENCODE-HEADER";
    protected static final String ATTR_DECODE = "DECODE-HEADER";

    // magic header.
    protected static final short    MAGIC              = (short) 0xdabb;
    
    protected static final byte     MAGIC_HIGH         = Bytes.short2bytes(MAGIC)[0];
    
    protected static final byte     MAGIC_LOW          = Bytes.short2bytes(MAGIC)[1];

    // message flag.
    protected static final byte     FLAG_REQUEST       = (byte) 0x80;

    protected static final byte     FLAG_TWOWAY        = (byte) 0x40;

    protected static final byte     FLAG_EVENT     = (byte) 0x20;

    //减少序列化器数量，添加一个bit为扩展协议标示
    protected static final byte    FLAG_EXTENSION = (byte)0x10;
    
    protected static final int      SERIALIZATION_MASK = 0x0f;
    protected Interceptor interceptor;
    public ExchangeCodec(){
    	this.interceptor = InterceptorUtils.getInterceptor();
    }

    public Short getMagicCode() {
        return MAGIC;
    }

    public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException {
        if (msg instanceof Request) {
            encodeRequest(channel, buffer, (Request) msg);
        } else if (msg instanceof Response) {
            encodeResponse(channel, buffer, (Response) msg);
        } else {
            super.encode(channel, buffer, msg);
        }
    }

    public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {
        int readable = buffer.readableBytes();
        if(readable == 0) return DecodeResult.NEED_MORE_INPUT;
        byte[] header = (byte[]) channel.getAttribute(ATTR_DECODE);
        int len = Math.min(readable, HEADER_LENGTH);
        if(header == null){
             header = new byte[HEADER_LENGTH_EX];
             channel.setAttribute(ATTR_DECODE, header);
        }
        header[0] = header[1] = 0;
        buffer.readBytes(header, 0, len);
        return decode(channel, buffer, readable, header, len);
    }
    
    @SuppressWarnings("unchecked")
	protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byte[] header,  int headerLength) throws IOException {
        // check magic number.
        if (readable > 0 && header[0] != MAGIC_HIGH 
                || readable > 1 && header[1] != MAGIC_LOW) {
            int length = headerLength;
            if (headerLength < readable) {
                header = Bytes.copyOf(header, readable);
                buffer.readBytes(header, length, readable - length);
            }
            for (int i = 1; i < headerLength - 1; i ++) {
                if (header[i] == MAGIC_HIGH && header[i + 1] == MAGIC_LOW) {
                    buffer.readerIndex(buffer.readerIndex() - headerLength + i);
                    header = Bytes.copyOf(header, i);
                    break;
                }
            }
            return super.decode(channel, buffer, readable, header);
        }
        boolean new_protocol = false;
        //old protocol  header + data
        //new protocol  header + datasize + data + header
        // check length.
        if (headerLength < HEADER_LENGTH) {
        	return DecodeResult.NEED_MORE_INPUT;
        }
        //native protocol?
        byte comp = header[2];
		if((comp & FLAG_EXTENSION) != 0){
			new_protocol = true;
		}
        // get total length.
        int total_len = Bytes.bytes2int(header, 12);
        checkPayload(channel, total_len);

        int tt = total_len + HEADER_LENGTH;
        if( readable < tt ) {
            return DecodeResult.NEED_MORE_INPUT;
        }
        int saveReaderIndex = buffer.readerIndex();
        //总长度后面是头部长度
        int data_len = total_len;
        int header_len = 0;
        if(new_protocol){
        	buffer.readBytes(header, 16, 4);
        	header_len = Bytes.bytes2int(header, 16);
        	//buffer.readerIndex(saveReaderIndex);
        	data_len = total_len - header_len - 4;
        }
        Map<String,String> headers = null;
       	byte flag = header[2], proto = (byte) (flag & SERIALIZATION_MASK);
       	//屏蔽事件（心跳）
       	Interceptor interceptor = (flag &  FLAG_EVENT) == 0?this.interceptor:null;
       	
       	if(interceptor != null && !interceptor.acceptAfterHead(channel.getRemoteAddress().getAddress(), total_len, data_len)){
			channel.close();
			return null;
    	}
       	
        Serialization s = CodecSupport.getSerialization(channel.getUrl(), proto);
        //decode attachments
        if(header_len > 0){
        	//已经跳过了protocol header
        	buffer.readerIndex(saveReaderIndex + 4);
        	ChannelBufferInputStream ais = new ChannelBufferInputStream(buffer, header_len);
             ObjectInput in = s.deserialize(channel.getUrl(), ais);
            try {
				headers = (Map<String,String>)in.readObject(Map.class);
			} catch (ClassNotFoundException e) {
				throw new IOException(e);
			}
        }
        if(headers == null){
        	headers = new HashMap<String, String>();
        }
        //when compatible native, data behind header, or skip 4 bytes length
        buffer.readerIndex(saveReaderIndex + (new_protocol?4:0) + header_len);
        // limit input stream.
        ChannelBufferInputStream is = null;
        if(data_len > 0){
        	is = new ChannelBufferInputStream(buffer, data_len);
        }
        if(interceptor != null && !interceptor.acceptAfterDecodeHeader(channel.getRemoteAddress().getAddress(), headers, data_len)){
			channel.close();
			return null;
    	}
        

        InputStream xis = null;
        try {
        	if(is != null){
	        	//模拟gzip解压
	         	MessageDecode[] decodes = headers == null?null:MessageCodecSupport.splitDecode(headers.get(MessageCodec.CODEC_KEY));
	         	//MessageEncodeRegistry registry = null;
	         	if(decodes != null && decodes.length > 0){
	                 //registry = new MessageEncodeRegistryImpl(headers);
	                 xis = new BridgeDecodeInputStream(new DecodeWrapper(decodes), is/*, registry*/);
	         	}
	             Object result = decodeBody(channel, xis == null?is:xis, header, headers, new_protocol);
	             /*
	         	 if(headers != null && registry != null){
	         		headers.putAll(registry.getHeaders());
	             }
	             */
	         	 
	         	if(interceptor != null){
	         		if(result instanceof RemoteCalling){
	         			if(!interceptor.acceptAfterDecodeBody(channel.getRemoteAddress().getAddress(), headers, (RemoteCalling)result)){
			    			channel.close();
			    			return null;
			        	}
	         		}else if(result instanceof RemoteCallingAware){
	         			RemoteCalling calling = ((RemoteCallingAware)result).getRemoteCalling();
	         			if(calling != null){
	         				if(!interceptor.acceptAfterDecodeBody(channel.getRemoteAddress().getAddress(),
	         						headers, calling)){
				    			channel.close();
				    			return null;
				        	}
	         			}
	         		}
	         	}
	         	//System.out.println("got result");
	         	//System.out.println("decode success:" + result.getClass());
	         	return result;
        	}else{
        		//数据为空，则原样返回
        		/*
        		try {
					channel.send(null);
				} catch (RemotingException e) {
					e.printStackTrace();
				}
				*/
        		return null;
        	}
        	
        	
            //return decodeBody(channel, is, header, attachments);
        } finally {
        	//恢复到正常数据流
        	buffer.readerIndex(saveReaderIndex + total_len);
        	if(xis != null){
        		xis.close();
        	}
            if (is != null && is.available() > 0) {
                try {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Skip input stream " + is.available());
                    }
                    StreamUtils.skipUnusedStream(is);
                } catch (IOException e) {
                    logger.warn(e.getMessage(), e);
                }
            }
        }
    }
    
    protected Serialization getSerialization(Channel channel, byte flag, Map<String,String> headers){
        byte  proto = (byte) (flag & SERIALIZATION_MASK);
        Serialization s = CodecSupport.getSerialization(channel.getUrl(), proto);    
    	return s;
    }

    protected Object decodeBody(Channel channel, InputStream is, byte[] header, Map<String,String> headers, boolean new_protocol) throws IOException {
    	byte flag = header[2];
    	Serialization s = getSerialization(channel, flag, headers);
    	ObjectInput in = s.deserialize(channel.getUrl(), is);
        // get request id.
        long id = Bytes.bytes2long(header, 4);
        if ((flag & FLAG_REQUEST) == 0) {
            // decode response.
            Response res = new Response(id);
            res.setNewProtocol(new_protocol);
            if ((flag & FLAG_EVENT) != 0) {
                res.setEvent(Response.HEARTBEAT_EVENT);
            }
            // get status.
            byte status = header[3];
            res.setStatus(status);
            if (status == Response.OK) {
                try {
                    Object data;
                    if (res.isHeartbeat()) {
                        data = decodeHeartbeatData(channel, in);
                    } else if (res.isEvent()) {
                        data = decodeEventData(channel, in);
                    } else {
                    	//模拟gzip解压
                        data = decodeResponseData(channel, in, getRequestData(id));
                    }
                    res.setResult(data);
                } catch (Throwable t) {
                    res.setStatus(Response.CLIENT_ERROR);
                    res.setErrorMessage(StringUtils.toString(t));
                }
            } else {
                res.setErrorMessage(in.readUTF());
            }
            return res;
        } else {
            // decode request.
            Request req = new Request(id);
            req.setNewProtocol(new_protocol);
            req.setVersion("2.0.0");
            req.setTwoWay((flag & FLAG_TWOWAY) != 0);
            if ((flag & FLAG_EVENT) != 0) {
                req.setEvent(Request.HEARTBEAT_EVENT);
            }
            try {
                Object data;
                if (req.isHeartbeat()) {
                    data = decodeHeartbeatData(channel, in);
                } else if (req.isEvent()) {
                    data = decodeEventData(channel, in);
                } else {
                	//模拟gzip解压
                    data = decodeRequestData(channel, in);
                }
                req.setData(data);
            } catch (Throwable t) {
                // bad request
                req.setBroken(true);
                req.setData(t);
            }
            return req;
        }
    }

    protected Object getRequestData(long id) {
        DefaultFuture future = DefaultFuture.getFuture(id);
        if (future == null)
            return null;
        Request req = future.getRequest();
        if (req == null)
            return null;
        return req.getData();
    }
    
    /**
     * 
     * @param channel
     * @param buffer
     * @param writerIndex
     * @param serialization
     * @param attachments
     * @return map object
     * @throws IOException
     */
    protected int encodeRequestHeaders(Channel channel, ChannelBuffer buffer, 
    		int writerIndex, Serialization serialization, Object data ) throws IOException{
    	 return encodeHeaders(channel, buffer, writerIndex, serialization, null);
    }
    
    /**
     * 
     * @param channel
     * @param buffer
     * @param writerIndex
     * @param serialization
     * @param attachments
     * @return map object
     * @throws IOException
     */
    protected int encodeResponseHeaders(Channel channel, ChannelBuffer buffer, 
    		int writerIndex, Serialization serialization, Object data ) throws IOException{
    	 return encodeHeaders(channel, buffer, writerIndex, serialization, null);
    }
    
    
    protected int encodeHeaders(Channel channel, ChannelBuffer buffer, 
    		int writerIndex, Serialization serialization, Map<String,String> headers) throws IOException{
    	
    	if(interceptor != null && !interceptor.acceptBeforeEncodeHeader(channel.getRemoteAddress().getAddress(), headers)){
 			channel.close();
 			return 0;
     	}
    	if(headers == null || headers.isEmpty()) return 0;
    	
    	int savedWriteIndex = buffer.writerIndex();
        buffer.writerIndex(writerIndex);
        try{
	        ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
	        ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
	        out.writeObject(headers);
	        out.flushBuffer();
	        bos.flush();
	        bos.close();
	        int len = bos.writtenBytes();
	        return len;
        }finally{
        	buffer.writerIndex(savedWriteIndex);
        }
    }

    protected Map<String, String> getRequestHeaders(Request req){
    	return null;
    }
    
    
    protected Map<String, String> getResponseHeaders(Response resp){
    	return null;
    }
    
    
    protected Serialization getSerialization(Channel channel, Request req){
    	Serialization serialization = getSerialization(channel);
    	return serialization;
    }
    
    protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
        Serialization serialization = getSerialization(channel, req);
        // header.
        byte[] header = (byte[]) channel.getAttribute(ATTR_ENCODE);
        if(header == null){
        	header = new byte[HEADER_LENGTH_EX];
        	channel.setAttribute(ATTR_ENCODE, header);
        }
        // set magic number.
        Bytes.short2bytes(MAGIC, header);
        
        // set request and serialization flag.
        header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());

        if (req.isTwoWay()) header[2] |= FLAG_TWOWAY;
        if (req.isEvent()) header[2] |= FLAG_EVENT;

        // set request id.
        Bytes.long2bytes(req.getId(), header, 4);
        
        // ecode request attachments, aruan 2014.09.15
        // 由于编码阶段可能修改attachments,所以encode attachments应该放在后面
        
        
        // encode request data.
        int savedWriteIndex = buffer.writerIndex();
        //先存放数据
        //buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + (req.isNewProtocol()?4:0));
        
        int headersSize = 0;
        if(!req.isEvent() && req.isNewProtocol()){
        	headersSize = encodeRequestHeaders(channel, buffer, 
        			savedWriteIndex + HEADER_LENGTH +  (req.isNewProtocol()?4:0), serialization, req.getData());
        }
        
        buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + (req.isNewProtocol()?4:0) + headersSize);
        ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
        //ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
        ObjectOutput out = null;
        
        if (req.isEvent()) {
        	out = serialization.serialize(channel.getUrl(), bos);
            encodeEventData(channel, out, req.getData());
            out.flushBuffer();
        } else {
            //模拟zip压缩，不压缩心跳包
        	if(req.isNewProtocol()){
	            Map<String, String> headers = getRequestHeaders(req);
	        	MessageEncode[] encodes = headers == null?null:MessageCodecSupport.splitEncode(headers.get(MessageCodec.CODEC_KEY));
	        	if(encodes != null && encodes.length > 0){
	                 //MessageEncodeRegistry registry = new MessageEncodeRegistryImpl(headers);
	                 OutputStream os = new BridgeEncodeOutputStream(new EncodeWrapper(encodes), bos/*, registry*/);
	             	 out = serialization.serialize(channel.getUrl(), os);
	             	 encodeRequestData(channel, out, req.getData());
	             	 out.flushBuffer();
	             	 os.close();
	             	 //headers.putAll(registry.getHeaders());
	             	 //headers.put(MessageCodec.CODEC_KEY, registry.getEncodesAsString());
	        	}else{
	        		//if(headers != null){
	        		//	headers.remove(MessageCodec.CODEC_KEY);
	        		//}
	        		out = serialization.serialize(channel.getUrl(), bos);
	        		encodeRequestData(channel, out, req.getData());
	        		out.flushBuffer();
	        	}
        	}else{
        		out = serialization.serialize(channel.getUrl(), bos);
        		encodeRequestData(channel, out, req.getData());
        		out.flushBuffer();
        	}
        }
        bos.flush();
        bos.close();
        int len = bos.writtenBytes();
        
        

        int tlen = headersSize + len + (req.isNewProtocol()?4:0) ;
        checkPayload(channel, tlen);
	
        //写入总长度
        Bytes.int2bytes(tlen, header, 12);
        if(req.isNewProtocol()){
        	//写入数据的长度
        	Bytes.int2bytes(headersSize, header, HEADER_LENGTH);
        	header[2] |= FLAG_EXTENSION;
        }
        
        // write
        buffer.writerIndex(savedWriteIndex);
        buffer.writeBytes(header, 0, req.isNewProtocol()? header.length:HEADER_LENGTH); // write header.
        //buffer.writeBytes(bos);
        buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + tlen);
    }

    protected Serialization getSerialization(Channel channel, Response res){
    	return getSerialization(channel);
    }
    
    protected void encodeResponse(Channel channel, ChannelBuffer buffer, Response res) throws IOException {
        int savedWriteIndex = buffer.writerIndex();
        try {
            Serialization serialization = getSerialization(channel, res);
            // header.
            byte[] header = (byte[]) channel.getAttribute(ATTR_ENCODE);
            if(header == null){
            	header = new byte[HEADER_LENGTH_EX];
            	channel.setAttribute(ATTR_ENCODE,  header);
            }
            // set magic number.
            Bytes.short2bytes(MAGIC, header);
            // set request and serialization flag.
            header[2] = serialization.getContentTypeId();
            if (res.isHeartbeat()) header[2] |= FLAG_EVENT;
            // set response status.
            byte status = res.getStatus();
            header[3] = status;
            // set request id.
            Bytes.long2bytes(res.getId(), header, 4);
            
            // ecode request attachments, aruan 2014.09.15
         // 由于编码阶段可能修改attachments,所以encode attachments应该放在后面
            

            //因为有attachments 所以要加上attachmentsSize的长度
            //buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + (res.isNewProtocol()?4:0));
            //ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
            ObjectOutput out = null;
            int headersSize = 0;
            if(!res.isHeartbeat() && res.isNewProtocol()){
            	buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + (res.isNewProtocol()?4:0));
            	headersSize = encodeResponseHeaders(channel, buffer, 
            				savedWriteIndex + HEADER_LENGTH + 4, serialization, res.getResult());
            }
            // encode response data or error message.
            buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + (res.isNewProtocol()?(4 + headersSize):0));
            ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
            if (status == Response.OK) {
                if (res.isHeartbeat()) {
                	out = serialization.serialize(channel.getUrl(), bos);
                    encodeHeartbeatData(channel, out, res.getResult());
                    out.flushBuffer();
                } else {
                	//模拟zip压缩 ，不压缩心跳包
                    Map<String, String> headers = getResponseHeaders(res);
                    if(interceptor != null && !interceptor.acceptBeforeEncodeBody(channel.getRemoteAddress().getAddress(), headers)){
             			channel.close();
             			return;
                 	}
                	MessageEncode[] encodes = headers == null?null:MessageCodecSupport.splitEncode(headers.get(MessageCodec.CODEC_KEY));
                	if(encodes != null && encodes.length > 0){
                         //MessageEncodeRegistry registry = new MessageEncodeRegistryImpl(headers);
                         OutputStream os = new BridgeEncodeOutputStream(new EncodeWrapper(encodes), bos/*, registry*/);
                     	 out = serialization.serialize(channel.getUrl(), os);
                     	 encodeResponseData(channel, out, res.getResult());
                     	 out.flushBuffer();
                     	 os.close();
                     	 /*
                     	 if(headers != null){
                     		headers.putAll(registry.getHeaders());
                     		headers.put(MessageCodec.CODEC_KEY, registry.getEncodesAsString());
                         }
                         */
                	}else{
                		out = serialization.serialize(channel.getUrl(), bos);
                		encodeResponseData(channel, out, res.getResult());
                		out.flushBuffer();
                	}
                }
            }
            else {
            	 if (res.isHeartbeat()) {
                 	out = serialization.serialize(channel.getUrl(), bos);
                 	out.writeUTF(res.getErrorMessage());
                 	out.flushBuffer();
                 } else {
                 	//模拟zip压缩 ，不压缩心跳包
                	 Map<String, String> headers = res.isNewProtocol()?getResponseHeaders(res):null;
                	 if(interceptor != null && !interceptor.acceptBeforeEncodeBody(channel.getRemoteAddress().getAddress(), headers)){
              			channel.close();
              			return;
                  	}
                 	MessageEncode[] encodes = headers == null?null:MessageCodecSupport.splitEncode(headers.get(MessageCodec.CODEC_KEY));
                 	if(encodes != null && encodes.length > 0 && res.isNewProtocol()){
                         //MessageEncodeRegistry registry = new MessageEncodeRegistryImpl(headers);
                         OutputStream os = new BridgeEncodeOutputStream(new EncodeWrapper(encodes), bos/*, registry*/);
                      	 out = serialization.serialize(channel.getUrl(), os);
                      	 encodeResponseData(channel, out, res.getResult());
                      	 out.flushBuffer();
                      	 os.close();
                      	 /*
                      	 if(headers != null){
                      		headers.putAll(registry.getHeaders());
                      		headers.put(MessageCodec.CODEC_KEY, registry.getEncodesAsString());
                          }
                          */
                 	}else{
                 		out = serialization.serialize(channel.getUrl(), bos);
                 		encodeResponseData(channel, out, res.getResult());
                 		out.flushBuffer();
                 	}
                 }
            }
            //out.flushBuffer();
            bos.flush();
            bos.close();

            int len = bos.writtenBytes();
            
            

            int tlen = len + headersSize + (res.isNewProtocol()?4:0);
            checkPayload(channel, tlen);
            //总长度
        	Bytes.int2bytes(tlen, header, 12);
            if(res.isNewProtocol()){
            	header[2] |= FLAG_EXTENSION;
            	//数据长度
            	Bytes.int2bytes(headersSize, header, HEADER_LENGTH);
            }
            
            // write
            buffer.writerIndex(savedWriteIndex);
            buffer.writeBytes(header, 0, res.isNewProtocol()?header.length:HEADER_LENGTH); // write header.
            buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + tlen);
        } catch (Throwable t) {
        	//恢复原写指针
        	buffer.writerIndex(savedWriteIndex);
            // 发送失败信息给Consumer，否则Consumer只能等超时了
            if (! res.isEvent() && res.getStatus() != Response.BAD_RESPONSE) {
                try {
                    // FIXME 在Codec中打印出错日志？在IoHanndler的caught中统一处理？
                    logger.warn("Fail to encode response: " + res + ", send bad_response info instead, cause: " + t.getMessage(), t);
                    
                    Response r = new Response(res.getId(), res.getVersion());
                    r.setStatus(Response.BAD_RESPONSE);
                    r.setErrorMessage("Failed to send response: " + res + ", cause: " + StringUtils.toString(t));
                    channel.send(r);
                    
                    return;
                } catch (RemotingException e) {
                    logger.warn("Failed to send bad_response info back: " + res + ", cause: " + e.getMessage(), e);
                }
            }
            
            // 重新抛出收到的异常
            if (t instanceof IOException) {
                throw (IOException) t;
            } else if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else if (t instanceof Error) {
                throw (Error) t;
            } else  {
                throw new RuntimeException(t.getMessage(), t);
            }
        }
    }
    
    @Override
    protected Object decodeData(ObjectInput in) throws IOException {
        return decodeRequestData(in);
    }

    @Deprecated
    protected Object decodeHeartbeatData(ObjectInput in) throws IOException {
        try {
            return in.readObject();
        } catch (ClassNotFoundException e) {
            throw new IOException(StringUtils.toString("Read object failed.", e));
        }
    }

    protected Object decodeRequestData(ObjectInput in) throws IOException {
        try {
            return in.readObject();
        } catch (ClassNotFoundException e) {
            throw new IOException(StringUtils.toString("Read object failed.", e));
        }
    }

    protected Object decodeResponseData(ObjectInput in) throws IOException {
        try {
            return in.readObject();
        } catch (ClassNotFoundException e) {
            throw new IOException(StringUtils.toString("Read object failed.", e));
        }
    }
    
    @Override
    protected void encodeData(ObjectOutput out, Object data) throws IOException {
        encodeRequestData(out, data);
    }
    
    private void encodeEventData(ObjectOutput out, Object data) throws IOException {
        out.writeObject(data);
    }
    
    @Deprecated
    protected void encodeHeartbeatData(ObjectOutput out, Object data) throws IOException {
        encodeEventData(out, data);
    }

    protected void encodeRequestData(ObjectOutput out, Object data) throws IOException {
        out.writeObject(data);
    }

    protected void encodeResponseData(ObjectOutput out, Object data) throws IOException {
        out.writeObject(data);
    }
    
    @Override
    protected Object decodeData(Channel channel, ObjectInput in) throws IOException {
        return decodeRequestData(channel ,in);
    }
    
    protected Object decodeEventData(Channel channel, ObjectInput in) throws IOException {
        try {
            return in.readObject();
        } catch (ClassNotFoundException e) {
            throw new IOException(StringUtils.toString("Read object failed.", e));
        }
    }

    @Deprecated
    protected Object decodeHeartbeatData(Channel channel, ObjectInput in) throws IOException {
        try {
            return in.readObject();
        } catch (ClassNotFoundException e) {
            throw new IOException(StringUtils.toString("Read object failed.", e));
        }
    }

    protected Object decodeRequestData(Channel channel, ObjectInput in) throws IOException {
        return decodeRequestData(in);
    }

    protected Object decodeResponseData(Channel channel, ObjectInput in) throws IOException {
        return decodeResponseData(in);
    }

    protected Object decodeResponseData(Channel channel, ObjectInput in, Object requestData) throws IOException {
        return decodeResponseData(channel, in);
    }
    
    @Override
    protected void encodeData(Channel channel, ObjectOutput out, Object data) throws IOException {
        encodeRequestData(channel, out, data);
    }

    private void encodeEventData(Channel channel, ObjectOutput out, Object data) throws IOException {
        encodeEventData(out, data);
    }
    @Deprecated
    protected void encodeHeartbeatData(Channel channel, ObjectOutput out, Object data) throws IOException {
        encodeHeartbeatData(out, data);
    }

    protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException {
        encodeRequestData(out, data);
    }

    protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException {
        encodeResponseData(out, data);
    }

}